/******************************************************************************

  Sega encryption emulation                                 by Nicola Salmoria


  Several Sega Z80 games have program ROMs encrypted using a common algorithm
  (but with a different key).
  The hardware used to implement this encryption is either a custom CPU, or an
  epoxy block which probably contains a standard Z80 + PALs.

  The encryption affects D3, D5, and D7, and depends on M1, A0, A4, A8 and A12.
  D0, D1, D3, D4 and D6 are always unaffected.

  The encryption consists of a permutation of the three bits, which can also be
  inverted. Therefore there are 3! * 2^3 = 48 different possible encryptions.

  For simplicity, the decryption is implemented using conversion tables.
  We need 32 of these tables, one for every possible combination of M1, A0, A4,
  A8 and A12. However, all the games currently known are full of repetitions
  and only use 6 different tables, the only exceptions being Pengo, Yamato and
  Spatter which have 7 (but one of them is the identity: { 0x00, 0x08, 0x20, 0x28 } ).
  This is most likely a limitation of the hardware.
  Some of the early games are even weaker: of the 6 different tables, they use
  3 for opcodes and 3 for data, and always coupled in the same way.

  In all games currently known, only bytes in the memory range 0x0000-0x7fff
  (A15 = 0) are encrypted. My guess is that this was done to allow games to
  copy code to RAM (in the memory range 0x8000-0xffff) and execute it from
  there without the CPU trying to decrypt it and messing everything up.
  However Zaxxon has RAM at 0x6000, and the CPU doesn't seem to interfere with
  it; but it doesn't execute code from there, so it's possible that the CPU is
  encrypting the data while writing it and decrypting it while reading (that
  would seem kind of strange though). Video and sprite RAM and memory mapped
  ports are all placed above 0x8000.

  Given its strict limitations, this encryption is reasonably easy to break,
  and very vulnerable to known plaintext attacks.



  Ninja Princess:

  there is a (bootleg?) board which has a standard Z80 + 2 bipolar PROMs
  instead of the custom CPU. The encryption table is different from the
  original Ninja Princess; it is actually the same as Flicky.

  The first PROM is 32x8 and contains the number (0..5) of the table to
  use depending on M1, A0, A4, A8, A12:

  00: 11 00 33 22 00 44 44 00 11 33 33 22 44 44 44 22
  10: 11 55 55 33 44 22 55 22 11 33 55 33 44 44 11 22

  The second PROM is 256x4 and contains the 6 different XOR tables:

       A  D  B  C  C  B  D  A
  00: 09 09 0A 0A 0A 0A 09 09
  08: 0E 08 0B 0D 0D 0B 08 0E
  10: 0A 0C 0A 0C 0C 0A 0C 0A
  18: 0B 0E 0E 0B 0B 0E 0E 0B
  20: 0C 0C 0F 0F 0F 0F 0C 0C
  28: 08 0D 0B 0E 0E 0B 0D 08
  [the remaining bytes are all 0F]
  bit 3 is not used.
  bits 0-2 is the XOR code inverted (0 = 0xa8, 1 = 0xa0 ... 6 = 0x08 7 = 0x00)

  Here is a diagram showing how it works:

  data to                             XOR
  decode                             value
                      A ---
  D7  --------------- 0|   |
  D3  --------------- 1|   |
  D5  --------------- 2| P |D
          A --- D      | R |0 ---|>--- D3
  M1  --- 0| P |0 --- 3| O |1 ---|>--- D5
  A0  --- 1| R |1 --- 4| M |2 ---|>--- D7
  A4  --- 2| O |2 --- 5| 2 |3 ---
  A8  --- 3| M |3 --- 6|   |
  A12 --- 4| 1 |4 --- 7|   |
            ---         ---


  My Hero:

  the bootleg does the decryption using a single 256x4 PROM, mapped in the
  obvious way:

  data to            XOR
  decode            value
          A ---
  D3  --- 0|   |
  D5  --- 1|   |D
  D7  --- 2| P |0 --- D3
  A0  --- 3| R |1 --- D5
  A4  --- 4| O |2 --- D7
  A8  --- 5| M |3 ---
  A12 --- 6|   |
  M1  --- 7|   |
            ---



  List of encrypted games currently known:

 CPU Part #         Game                   Comments
  ???-????      Buck Rogers            unencrypted version available
  315-5010      Pengo                  unencrypted version available
  315-5013      Super Zaxxon           used Zaxxon for known plaintext attack
  ???-????      Super Locomotive
  315-5018      Yamato
  315-5028      Sindbad Mystery
  315-5030      Up'n Down &            unencrypted version available
           M120 Razzmatazz
  315-5033      Regulus                unencrypted version available
  315-5041 M140 Mister Viking
  315-5048      SWAT                   used Bull Fight for k.p.a.
  315-5051      Flicky &
                Ninja Princess (bootleg)
  315-5061      Future Spy
  315-5064      Water Match            used Mister Viking for k.p.a.
  315-5065      Bull Fight
  315-5069      Star Force             game by Tehkan; same key as Super Locomotive
  ???-????      Spatter
  315-5093      Pitfall II
  315-5098      Ninja Princess         unencrypted version available; same key as Up'n Down
  315-5102      Sega Ninja             unencrypted version available
  315-5110      I'm Sorry              used My Hero for k.p.a.
  315-5114      ?? pcb 834-5492        same key as Regulus
  315-5115      TeddyBoy Blues
  315-5132      My Hero
  315-5135      Heavy Metal &
                Wonder Boy (set 1a & 3; bootlegs?)
  ???-????      Lovely Cards


  The following games use a different encryption algorithm:

  315-5162      4D Warriors &          used I'm Sorry for k.p.a.
                Rafflesia
  315-5177      Astro Flash &
                Wonder Boy (set 1)
  315-5178      Wonder Boy (set 2)     unencrypted version available
  315-5179      Robo-Wrestle 2001      not decoded yet


  The following games use another different encryption algorithm, much more
  secure than the previous ones, which has not been broken yet. It might be
  similar to the one used in System 16 games.

  317-5012      Ganbare Chinsan Ooshoubu  MC-8123A
  317-0014      DakkoChan Jansoh
  317-0029      Block Gal              NEC MC8123B 651 packaged like System16's 68000
  317-0030      Perfect Billiard
  317-0042      Opa Opa
  317-0043      Wonder Boy Monster Land
  317-0054      Shinobi (sound CPU)    NEC MC8123B 651
  317-0057      Fantasy Zone 2
  317-0064      Ufo Senshi Yohko Chan
                Ninja Kid II sound program also seems to use this encryption


  Some text found in the ROMs:

  Buck Rogers      SECULITY BY MASATOSHI,MIZUNAGA
  Super Locomotive SEGA FUKUMURA MIZUNAGA
  Yamato           SECULITY BY M,MIZUNAGA
  Regulus          SECULITY BY SYUICHI,KATAGI
  Up'n Down        19/SEP 1983   MASATOSHI,MIZUNAGA
  Mister Viking    SECURITY BY S.KATAGI  CONTROL CHIP M140
  SWAT             SECURITY BY S.KATAGI
  Flicky           SECURITY BY S.KATAGI
  Water Match      PROGRAMED BY KAWAHARA&NAKAGAWA
  Star Force       STAR FORCE TEHKAN. SECURITY BY SEGA ENTERPRISESE

******************************************************************************/

#include "driver.h"


#ifdef MAME_DEBUG
static void lfkp(int mask)
{
	int A;
	unsigned char *RAM = memory_region(REGION_CPU1);


	for (A = 0x0000;A < 0x8000-14;A++)
	{
		static char text[] = "INSERT COIN";
		int i;


		if (	(RAM[A+0] & mask) == (0x21 & mask) &&	/* LD HL,$xxxx */
				(RAM[A+3] & mask) == (0x11 & mask) &&	/* LD DE,$xxxx */
				(RAM[A+6] & mask) == (0x01 & mask))		/* LD BC,$xxxx */
		{
			if (	(RAM[A+ 9] & mask) == (0x36 & mask) &&	/* LD (HL),$xx */
					(RAM[A+11] & mask) == (0xed & mask) &&
					(RAM[A+12] & mask) == (0xb0 & mask))	/* LDIR */
				log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: hl de bc (hl),xx ldir\n",A);

			if (	(RAM[A+ 9] & mask) == (0x77 & mask) &&	/* LD (HL),A */
					(RAM[A+10] & mask) == (0xed & mask) &&
					(RAM[A+11] & mask) == (0xb0 & mask))	/* LDIR */
				log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: hl de bc (hl),a ldir\n",A);

			if (	(RAM[A+ 9] & mask) == (0xed & mask) &&
					(RAM[A+10] & mask) == (0xb0 & mask))	/* LDIR */
				log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: hl de bc ldir\n",A);
		}

		/* the following can also be PUSH IX, PUSH IY - need better checking */
		if (	(RAM[A+0] & mask) == (0xf5 & mask) &&	/* PUSH AF */
				(RAM[A+1] & mask) == (0xc5 & mask) &&	/* PUSH BC */
				(RAM[A+2] & mask) == (0xd5 & mask) &&	/* PUSH DE */
				(RAM[A+3] & mask) == (0xe5 & mask))		/* PUSH HL */
			log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: push af bc de hl\n",A);

		if (	(RAM[A+0] & mask) == (0xe1 & mask) &&	/* POP HL */
				(RAM[A+1] & mask) == (0xd1 & mask) &&	/* POP DE */
				(RAM[A+2] & mask) == (0xc1 & mask) &&	/* POP BC */
				(RAM[A+3] & mask) == (0xf1 & mask))		/* POP AF */
			log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: pop hl de bc af\n",A);

		for (i = 0;i < strlen(text);i++)
			if ((RAM[A+i] & mask) != (text[i] & mask)) break;
		if (i == strlen(text))
			log_cb(RETRO_LOG_DEBUG, LOGPRE "%04x: INSERT COIN\n",A);
	}
}

static void look_for_known_plaintext(void)
{
	lfkp(0x57);
}

static void read_table_from_disk(unsigned char *xortable)
{
	FILE *f;


	f = fopen("table","rb");

	if (f) fread(xortable,1,128,f);

	fclose(f);
}
#endif

static void sega_decode(const unsigned char convtable[32][4])
{
	int A;
	unsigned char *rom = memory_region(REGION_CPU1);
	int diff = memory_region_length(REGION_CPU1) / 2;

{
	int i;
	for (i = 0;i < 32;i++)
	{
		if ((convtable[i][0] & 8) != (convtable[i][1] & 8) &&
				(convtable[i][1] & 8) == (convtable[i][2] & 8))
		{
			printf("%02x %02x %02x %02x\n",
					convtable[i][0],
					convtable[i][1],
					convtable[i][2],
					convtable[i][3]);
			exit(0);
		}
	}
}

	memory_set_opcode_base(0,rom+diff);

	for (A = 0x0000;A < 0x8000;A++)
	{
		int row,col,xor = 0;
		unsigned char src;


		src = rom[A];

		/* pick the translation table from bits 0, 4, 8 and 12 of the address */
		row = (A & 1) + (((A >> 4) & 1) << 1) + (((A >> 8) & 1) << 2) + (((A >> 12) & 1) << 3);

		/* pick the offset in the table from bits 3 and 5 of the source data */
		col = ((src >> 3) & 1) + (((src >> 5) & 1) << 1);
		/* the bottom half of the translation table is the mirror image of the top */
		if (src & 0x80)
		{
			col = 3 - col;
			xor = 0xa8;
		}

		/* decode the opcodes */
		rom[A + diff] = (src & ~0xa8) | (convtable[2*row][col] ^ xor);

		/* decode the data */
		rom[A] = (src & ~0xa8) | (convtable[2*row+1][col] ^ xor);

		if (convtable[2*row][col] == 0xff)	/* table incomplete! (for development) */
			rom[A + diff] = 0x00;
		if (convtable[2*row+1][col] == 0xff)	/* table incomplete! (for development) */
			rom[A] = 0xee;
	}

	/* copy the opcodes from the not encrypted part of the ROMs */
	for (A = 0x8000;A < diff;A++)
		rom[A + diff] = rom[A];
}



void buckrog_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...0...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...0...0...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...1...0 */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...0...1...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...1...0...0 */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...1...0...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...1...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...1...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x88,0x80 },	/* ...1...0...0...0 */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...0...0...1 */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...1...1 */
		{ 0x80,0x00,0x88,0x08 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x88,0x80 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void pengo_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...0...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...0...1...0 */
		{ 0x08,0x28,0x88,0xa8 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...1 */
		{ 0x08,0x00,0x88,0x80 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...1...0...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x08,0x00,0x88,0x80 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...1...1...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x00,0x08,0x20,0x28 },	/* ...0...1...1...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x00,0x08,0x20,0x28 },	/* ...1...0...0...1 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x08,0x28,0x88,0xa8 },	/* ...1...0...1...0 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...1...1 */
		{ 0x08,0x00,0x88,0x80 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...0 */
		{ 0x00,0x08,0x20,0x28 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...1 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x08,0x28,0x88,0xa8 },	/* ...1...1...1...0 */
		{ 0x08,0x00,0x88,0x80 }, { 0xa0,0x80,0x20,0x00 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void szaxxon_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...0...0...0 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...1 */
		{ 0xa8,0x28,0xa0,0x20 }, { 0x20,0xa0,0x00,0x80 },	/* ...0...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...0...1...1 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...1...0...1 */
		{ 0xa8,0x28,0xa0,0x20 }, { 0x20,0xa0,0x00,0x80 },	/* ...0...1...1...0 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...1...1 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...0...0...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...0...1...0 */
		{ 0xa8,0x28,0xa0,0x20 }, { 0x20,0xa0,0x00,0x80 },	/* ...1...0...1...1 */
		{ 0xa8,0x28,0xa0,0x20 }, { 0x20,0xa0,0x00,0x80 },	/* ...1...1...0...0 */
		{ 0xa8,0x28,0xa0,0x20 }, { 0x20,0xa0,0x00,0x80 },	/* ...1...1...0...1 */
		{ 0x08,0x28,0x88,0xa8 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void suprloco_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...0...0 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...0...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...1...0...0 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...1...0...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...1...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...1...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...1...0...0...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...0...0...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...0...1...0 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...1...0...1...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...1...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...1...1...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0xa8,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void yamato_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0xa8,0x08,0x28 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...0...0 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...0...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x20,0xa0,0x28,0xa8 },	/* ...0...0...1...1 */
		{ 0x88,0xa8,0x08,0x28 }, { 0x88,0xa8,0x08,0x28 },	/* ...0...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...1...0...1 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x20,0xa0,0x28,0xa8 },	/* ...0...1...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...1...1...1 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x88,0xa8,0x08,0x28 },	/* ...1...0...0...0 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...0...0...1 */
		{ 0xa0,0x20,0x80,0x00 }, { 0x20,0xa0,0x28,0xa8 },	/* ...1...0...1...0 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0x20,0xa0,0x28,0xa8 },	/* ...1...0...1...1 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x88,0xa8,0x08,0x28 },	/* ...1...1...0...0 */
		{ 0x88,0xa8,0x08,0x28 }, { 0x88,0xa8,0x08,0x28 },	/* ...1...1...0...1 */
		{ 0xa0,0x20,0x80,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...1...1...1...0 */
		{ 0x20,0xa0,0x28,0xa8 }, { 0x00,0x08,0x20,0x28 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void sindbadm_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...0 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...0...0...1 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...0...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...1...1 */
		{ 0xa8,0x88,0xa0,0x80 }, { 0xa0,0x20,0xa8,0x28 },	/* ...0...1...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...1 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...1...1...0 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...1...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...1 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...1...0...1...0 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x00,0x20,0x80,0xa0 },	/* ...1...0...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...0 */
		{ 0xa8,0x88,0xa0,0x80 }, { 0xa0,0x20,0xa8,0x28 },	/* ...1...1...0...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void regulus_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x28,0x08,0xa8,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...0 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...0...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...0...1...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...0...1...1...0 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...1...1...1 */
		{ 0x80,0xa0,0x00,0x20 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...0...1 */
		{ 0x80,0xa0,0x00,0x20 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...0...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...0...1...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...1...0...0 */
		{ 0x80,0xa0,0x00,0x20 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...1...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...1...1...0 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void mrviking_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...0...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...1...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...1...0 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...1...1...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...1...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...1...0...1...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...1...1...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0xa8,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void swat_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...0...0...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...1...0 */
		{ 0xa0,0xa8,0x80,0x88 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...1...1 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...1...1...0 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...1...1...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...0...0 */
		{ 0xa0,0x20,0x80,0x00 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...0...1 */
		{ 0xa0,0x20,0x80,0x00 }, { 0xa0,0x20,0x80,0x00 },	/* ...1...0...1...0 */
		{ 0xa0,0x20,0x80,0x00 }, { 0xa0,0x20,0x80,0x00 },	/* ...1...0...1...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...1...1...0...1 */
		{ 0xa0,0xa8,0x80,0x88 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...1...0 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0xa0,0xa8,0x80,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void flicky_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x08,0x88,0x00,0x80 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0x08,0x20,0x00 },	/* ...0...0...1...0 */
		{ 0x28,0x08,0x20,0x00 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...1 */
		{ 0x08,0x88,0x00,0x80 }, { 0x80,0x00,0xa0,0x20 },	/* ...0...1...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...1 */
		{ 0x28,0x08,0x20,0x00 }, { 0x28,0x08,0x20,0x00 },	/* ...0...1...1...0 */
		{ 0x28,0x08,0x20,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...1...1 */
		{ 0x08,0x88,0x00,0x80 }, { 0xa8,0x88,0x28,0x08 },	/* ...1...0...0...0 */
		{ 0xa8,0x88,0x28,0x08 }, { 0x80,0x00,0xa0,0x20 },	/* ...1...0...0...1 */
		{ 0x28,0x08,0x20,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...1...0 */
		{ 0xa8,0x88,0x28,0x08 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...1...1 */
		{ 0x08,0x88,0x00,0x80 }, { 0x80,0x00,0xa0,0x20 },	/* ...1...1...0...0 */
		{ 0xa8,0x88,0x28,0x08 }, { 0x80,0x00,0xa0,0x20 },	/* ...1...1...0...1 */
		{ 0x28,0x08,0x20,0x00 }, { 0x28,0x08,0x20,0x00 },	/* ...1...1...1...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0x88,0x80,0x08,0x00 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void futspy_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x28,0x08,0x20,0x00 }, { 0x28,0x08,0x20,0x00 },	/* ...0...0...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x08,0x88,0x00,0x80 },	/* ...0...0...0...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x08,0x88,0x00,0x80 },	/* ...0...0...1...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x20,0x28,0xa0,0xa8 },	/* ...0...0...1...1 */
		{ 0x28,0x08,0x20,0x00 }, { 0x88,0x80,0xa8,0xa0 },	/* ...0...1...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x08,0x88,0x00,0x80 },	/* ...0...1...0...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x20,0x28,0xa0,0xa8 },	/* ...0...1...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x08,0x88,0x00,0x80 },	/* ...0...1...1...1 */
		{ 0x88,0x80,0xa8,0xa0 }, { 0x28,0x08,0x20,0x00 },	/* ...1...0...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...0...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x08,0x88,0x00,0x80 },	/* ...1...0...1...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...0...1...1 */
		{ 0x88,0x80,0xa8,0xa0 }, { 0x88,0x80,0xa8,0xa0 },	/* ...1...1...0...0 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x08,0x88,0x00,0x80 },	/* ...1...1...0...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x28,0x08,0x20,0x00 },	/* ...1...1...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0xa0,0x80,0x20,0x00 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void wmatch_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...0...0...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...0...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x20,0x28,0xa0,0xa8 },	/* ...0...0...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...0...1...1 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...1...0...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...1...0...1 */
		{ 0xa8,0x28,0x88,0x08 }, { 0x20,0x28,0xa0,0xa8 },	/* ...0...1...1...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...1...1...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...0...0...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...0...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...0...1...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...1...0...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...1...1...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void bullfgtj_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0xa0,0xa8,0x20,0x28 }, { 0x80,0xa0,0x00,0x20 },	/* ...0...0...0...0 */
		{ 0x20,0x28,0x00,0x08 }, { 0x20,0x28,0x00,0x08 },	/* ...0...0...0...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0x08,0x28,0x00,0x20 },	/* ...0...0...1...0 */
		{ 0x88,0x08,0xa8,0x28 }, { 0x88,0x08,0xa8,0x28 },	/* ...0...0...1...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0x20,0x28,0x00,0x08 },	/* ...0...1...0...0 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0x20,0x28,0x00,0x08 },	/* ...0...1...0...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0x08,0x28,0x00,0x20 },	/* ...0...1...1...0 */
		{ 0x88,0x08,0xa8,0x28 }, { 0x88,0x08,0xa8,0x28 },	/* ...0...1...1...1 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0xa0,0xa8,0x20,0x28 },	/* ...1...0...0...0 */
		{ 0x88,0x08,0xa8,0x28 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...0...0...1 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0x08,0x28,0x00,0x20 },	/* ...1...0...1...0 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...0...1...1 */
		{ 0x20,0x28,0x00,0x08 }, { 0x20,0x28,0x00,0x08 },	/* ...1...1...0...0 */
		{ 0x88,0x08,0xa8,0x28 }, { 0x20,0x28,0x00,0x08 },	/* ...1...1...0...1 */
		{ 0x08,0x28,0x00,0x20 }, { 0x80,0xa0,0x00,0x20 },	/* ...1...1...1...0 */
		{ 0x08,0x28,0x00,0x20 }, { 0x88,0x08,0xa8,0x28 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void spatter_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0x08,0x80,0x00 }, { 0x00,0x08,0x20,0x28 },	/* ...0...0...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...0...0...1 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...0...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...1...1 */
		{ 0x00,0x08,0x20,0x28 }, { 0x88,0x08,0x80,0x00 },	/* ...0...1...0...0 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x80,0x88,0x00,0x08 },	/* ...0...1...0...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...1...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x28,0x20,0xa8,0xa0 },	/* ...0...1...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x80,0x88,0x00,0x08 },	/* ...1...0...0...0 */
		{ 0x80,0x88,0x00,0x08 }, { 0x00,0x08,0x20,0x28 },	/* ...1...0...0...1 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0x28,0xa8,0x08,0x88 },	/* ...1...0...1...0 */
		{ 0x00,0x08,0x20,0x28 }, { 0x80,0xa0,0x88,0xa8 },	/* ...1...0...1...1 */
		{ 0x80,0x88,0x00,0x08 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...0 */
		{ 0x80,0xa0,0x88,0xa8 }, { 0xa0,0x80,0x20,0x00 },	/* ...1...1...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0x80,0xa0,0x88,0xa8 },	/* ...1...1...1...0 */
		{ 0x28,0x20,0xa8,0xa0 }, { 0x00,0x08,0x20,0x28 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void pitfall2_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...0...0 */
		{ 0x08,0x88,0x28,0xa8 }, { 0x28,0xa8,0x20,0xa0 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0xa8,0x20,0x28 },	/* ...0...0...1...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x20,0x00,0xa0,0x80 },	/* ...0...1...0...0 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0x20,0x00,0xa0,0x80 },	/* ...0...1...0...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0xa8,0x20,0x28 },	/* ...0...1...1...0 */
		{ 0x28,0xa8,0x20,0xa0 }, { 0xa0,0xa8,0x20,0x28 },	/* ...0...1...1...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x80,0x88,0xa0,0xa8 },	/* ...1...0...0...0 */
		{ 0x80,0x88,0xa0,0xa8 }, { 0x80,0x88,0xa0,0xa8 },	/* ...1...0...0...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...0...1...0 */
		{ 0x80,0x88,0xa0,0xa8 }, { 0x28,0xa8,0x20,0xa0 },	/* ...1...0...1...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x80,0x88,0xa0,0xa8 },	/* ...1...1...0...0 */
		{ 0x80,0x88,0xa0,0xa8 }, { 0x20,0x00,0xa0,0x80 },	/* ...1...1...0...1 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...1...1...0 */
		{ 0x80,0x88,0xa0,0xa8 }, { 0x28,0xa8,0x20,0xa0 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void nprinces_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x08,0x88,0x00,0x80 }, { 0xa0,0x20,0x80,0x00 },	/* ...0...0...0...0 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...0...1...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x20,0x80,0x00 },	/* ...0...1...0...0 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0xa8,0xa0,0x28,0x20 },	/* ...0...1...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...1...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...1...1...1 */
		{ 0xa0,0x20,0x80,0x00 }, { 0xa0,0x20,0x80,0x00 },	/* ...1...0...0...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...0...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...1...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...1...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...1...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...1...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0x28,0x08,0xa8,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void seganinj_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...0...0...1 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0xa8,0xa0,0x28,0x20 },	/* ...0...0...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa0,0xa8,0x80,0x88 },	/* ...0...0...1...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...0...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x88,0x08,0x80,0x00 },	/* ...0...1...0...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...1...0 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x28,0x20 },	/* ...0...1...1...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...0...0 */
		{ 0xa0,0xa8,0x80,0x88 }, { 0x28,0xa8,0x08,0x88 },	/* ...1...0...0...1 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...1...0 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0x28,0xa8,0x08,0x88 },	/* ...1...0...1...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...1...0...0 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...1...0...1 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...1...1...0 */
		{ 0xa8,0xa0,0x28,0x20 }, { 0x28,0x08,0xa8,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void imsorry_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0x08,0x80,0x00 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...0...0...0 */
		{ 0x00,0x20,0x80,0xa0 }, { 0x88,0x08,0x80,0x00 },	/* ...0...0...0...1 */
		{ 0x88,0x08,0xa8,0x28 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...0...1...0 */
		{ 0x00,0x20,0x80,0xa0 }, { 0x88,0x08,0xa8,0x28 },	/* ...0...0...1...1 */
		{ 0x00,0x20,0x80,0xa0 }, { 0x08,0x00,0x88,0x80 },	/* ...0...1...0...0 */
		{ 0x00,0x20,0x80,0xa0 }, { 0x20,0x28,0xa0,0xa8 },	/* ...0...1...0...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x00,0x20,0x80,0xa0 },	/* ...0...1...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x88,0x08,0xa8,0x28 },	/* ...0...1...1...1 */
		{ 0x88,0x08,0x80,0x00 }, { 0x08,0x00,0x88,0x80 },	/* ...1...0...0...0 */
		{ 0x08,0x00,0x88,0x80 }, { 0x88,0x08,0x80,0x00 },	/* ...1...0...0...1 */
		{ 0x08,0x28,0x00,0x20 }, { 0x08,0x28,0x00,0x20 },	/* ...1...0...1...0 */
		{ 0x88,0x08,0x80,0x00 }, { 0x08,0x28,0x00,0x20 },	/* ...1...0...1...1 */
		{ 0x08,0x28,0x00,0x20 }, { 0x08,0x00,0x88,0x80 },	/* ...1...1...0...0 */
		{ 0x08,0x28,0x00,0x20 }, { 0x20,0x28,0xa0,0xa8 },	/* ...1...1...0...1 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x08,0x28,0x00,0x20 },	/* ...1...1...1...0 */
		{ 0x20,0x28,0xa0,0xa8 }, { 0x08,0x28,0x00,0x20 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void teddybb_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x20,0x28,0x00,0x08 }, { 0x80,0x00,0xa0,0x20 },	/* ...0...0...0...0 */
		{ 0x20,0x28,0x00,0x08 }, { 0xa0,0xa8,0x20,0x28 },	/* ...0...0...0...1 */
		{ 0x28,0x08,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...1...1 */
		{ 0x20,0x28,0x00,0x08 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...0...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0xa8,0x20,0x28 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...1...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...1...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x80,0x00,0xa0,0x20 },	/* ...1...0...0...0 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0xa0,0xa8,0x20,0x28 },	/* ...1...0...0...1 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...0...1...0 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...0...1...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0x20,0x28,0x00,0x08 },	/* ...1...1...0...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0x20,0xa8,0x28 },	/* ...1...1...0...1 */
		{ 0x80,0x00,0xa0,0x20 }, { 0xa0,0x80,0xa8,0x88 },	/* ...1...1...1...0 */
		{ 0xa0,0xa8,0x20,0x28 }, { 0xa0,0x20,0xa8,0x28 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void myheroj_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x20,0x00,0xa0,0x80 }, { 0x80,0xa0,0x88,0xa8 },	/* ...0...0...0...0 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x80,0xa0,0x88,0xa8 },	/* ...0...0...0...1 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...1...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0x80,0xa0,0x88,0xa8 },	/* ...0...0...1...1 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x28,0xa8,0x08,0x88 },	/* ...0...1...0...0 */
		{ 0x20,0x00,0xa0,0x80 }, { 0x08,0x88,0x00,0x80 },	/* ...0...1...0...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...1...1...0 */
		{ 0x08,0x88,0x00,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...1...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0x20,0x00,0xa0,0x80 },	/* ...1...0...0...0 */
		{ 0x80,0xa0,0x88,0xa8 }, { 0x20,0x00,0xa0,0x80 },	/* ...1...0...0...1 */
		{ 0x80,0xa0,0x88,0xa8 }, { 0x80,0xa0,0x88,0xa8 },	/* ...1...0...1...0 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0x80,0xa0,0x88,0xa8 },	/* ...1...0...1...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x08,0x88,0x00,0x80 },	/* ...1...1...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0xa8,0xa0,0x88,0x80 },	/* ...1...1...1...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0xa8,0xa0,0x88,0x80 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void hvymetal_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0x88,0xa8,0x80,0xa0 }, { 0xa0,0x80,0xa8,0x88 },	/* ...0...0...0...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x88,0xa8,0x80,0xa0 },	/* ...0...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x88,0x80,0x08,0x00 },	/* ...0...0...1...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0xa8,0x88 }, { 0x88,0x80,0x08,0x00 },	/* ...0...1...1...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x28,0x08,0xa8,0x88 },	/* ...0...1...1...1 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...0...0 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...0...1 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0x88,0xa8,0x80,0xa0 },	/* ...1...0...1...0 */
		{ 0x88,0xa8,0x80,0xa0 }, { 0x28,0x08,0xa8,0x88 },	/* ...1...0...1...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa0,0x20,0xa8,0x28 },	/* ...1...1...0...0 */
		{ 0xa0,0x20,0xa8,0x28 }, { 0x28,0xa8,0x08,0x88 },	/* ...1...1...0...1 */
		{ 0x28,0xa8,0x08,0x88 }, { 0xa0,0x20,0xa8,0x28 },	/* ...1...1...1...0 */
		{ 0x28,0x08,0xa8,0x88 }, { 0x28,0xa8,0x08,0x88 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


void lvcards_decode(void)
{
	static const unsigned char convtable[32][4] =
	{
		/*       opcode                   data                     address      */
		/*  A    B    C    D         A    B    C    D                           */
		{ 0xa8,0x88,0xa0,0x80 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...0...0...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...0...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...0...1...0 */
		{ 0xa8,0xa0,0x88,0x80 }, { 0xa8,0xa0,0x88,0x80 },	/* ...0...0...1...1 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0xa0,0x20 },	/* ...0...1...0...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 },	/* ...0...1...0...1 */
		{ 0xa0,0x80,0x20,0x00 }, { 0xa0,0x80,0x20,0x00 },	/* ...0...1...1...0 */
		{ 0xa8,0x88,0xa0,0x80 }, { 0xa8,0x88,0xa0,0x80 },	/* ...0...1...1...1 */
		{ 0xa8,0x88,0xa0,0x80 }, { 0xa8,0x28,0x88,0x08 },	/* ...1...0...0...0 */
		{ 0x88,0x80,0x08,0x00 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...0...1 */
		{ 0xa8,0x28,0x88,0x08 }, { 0x88,0x80,0x08,0x00 },	/* ...1...0...1...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x28,0x88,0x08 },	/* ...1...0...1...1 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x88,0xa0,0x80 },	/* ...1...1...0...0 */
		{ 0xa8,0x88,0xa0,0x80 }, { 0x88,0x80,0x08,0x00 },	/* ...1...1...0...1 */
		{ 0x88,0x80,0x08,0x00 }, { 0xa8,0x28,0x88,0x08 },	/* ...1...1...1...0 */
		{ 0xa8,0x28,0x88,0x08 }, { 0xa8,0x88,0xa0,0x80 }	/* ...1...1...1...1 */
	};


	sega_decode(convtable);
}


/******************************************************************************

  4D Warriors

  This encryption is quite different from the standard one. It permutates bits
  D0, D2, D4 and D6 without inverting them, and adds a XOR on top.

  The permutation depends on A9, A12, and A14, while the XOR depends on A0, A3,
  and A6. Opcodes are XORed with an additional 0x40.

  There are no separate tables for data and opcodes; to make it a little more
  complicated, however, to decode data you have to add 1 to [A14 A12 A9 A6 A3 A0].
  For example if you are decoding a byte at address .1.0..1..1..1..1, you pick
  the permutation and XOR as if it were at address .1.1..0..0..0..0

******************************************************************************/

void fdwarrio_decode(void)
{
	static const unsigned char swaptable[8+1][4] =
	{
		/* note that this is a subset of the astrofl table */
		{ 6,2,4,0 },	/* .0.0..0..x..x..x */
		{ 6,0,2,4 },	/* .0.0..1..x..x..x */
		{ 6,4,0,2 },	/* .0.1..0..x..x..x */
		{ 2,6,4,0 },	/* .0.1..1..x..x..x */
		{ 4,2,6,0 },	/* .1.0..0..x..x..x */
		{ 4,6,0,2 },	/* .1.0..1..x..x..x */
		{ 6,0,4,2 },	/* .1.1..0..x..x..x */
		{ 0,6,4,2 },	/* .1.1..1..x..x..x */
		{ 4,0,6,2 }		/* extra line for data decode */
	};
	int A;
	unsigned char *rom = memory_region(REGION_CPU1);
	int diff = memory_region_length(REGION_CPU1) / 2;


	memory_set_opcode_base(0,rom+diff);

	for (A = 0x0000;A < 0x8000;A++)
	{
		int row;
		unsigned char src;
		const unsigned char *tbl;


		src = rom[A];

		/* pick the translation table from bits 0, 3, 6, 9, 12 and 14 of the address */
		row = (A & 1) + (((A >> 3) & 1) << 1) + (((A >> 6) & 1) << 2)
				+ (((A >> 9) & 1) << 3) + (((A >> 12) & 1) << 4) + (((A >> 14) & 1) << 5);

		/* decode the opcodes */
		tbl = swaptable[row >> 3];
		rom[A + diff] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ 0x40;
		if (row & 1) rom[A + diff] ^= 0x10;
		if (row & 2) rom[A + diff] ^= 0x04;
		if (row & 4) rom[A + diff] ^= 0x01;

		/* decode the data */
		row++;	/* the data permutation table is shifted by one position!!!! */
		tbl = swaptable[row >> 3];
		rom[A] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]);
		if (row & 1) rom[A] ^= 0x10;
		if (row & 2) rom[A] ^= 0x04;
		if (row & 4) rom[A] ^= 0x01;
	}

	/* copy the opcodes from the not encrypted part of the ROMs */
	for (A = 0x8000;A < diff;A++)
		rom[A + diff] = rom[A];
}



/******************************************************************************

  Astro Flash / Wonder Boy

  This is different again. It affects the same data bits and is selected by the
  same address lines as 4D Warriors, but the permutation and XOR values to use
  are selected in a much more complicated way. The XOR values repeat with a
  period of 12 (!!!), but the count restarts from 0 when A14 is set.
  The permutation is selected so that only 5 different permutations are used
  for 12 consecutive address masks.
  Like in 4D Warriors, the tables for opcodes and data are slightly misaligned.

******************************************************************************/

void astrofl_decode(void)
{
	static unsigned char opcode_xor[32] =
	{
		0x04,0x51,0x40,0x01,0x55,0x44,0x05,0x50,0x41,0x00,0x54,0x45,0x04,0x51,0x40,0x01,
		0x55,0x44,0x05,0x50,0x41,0x00,0x54,0x45,0x04,0x51,0x40,0x01,0x55,0x44,0x05,0x50,
	};

	static unsigned char data_xor[32] =
	{
		0x54,0x15,0x44,0x51,0x10,0x41,0x55,0x14,0x45,0x50,0x11,0x40,0x54,0x15,0x44,0x51,
		0x10,0x41,0x55,0x14,0x45,0x50,0x11,0x40,0x54,0x15,0x44,0x51,0x10,0x41,0x55,0x14,
	};

	static const int opcode_swap_select[32] =
	{
		0,0,1,1,1,2,2,3,3,4,4,4,5,5,6,6,
		6,7,7,8,8,9,9,9,10,10,11,11,11,12,12,13,
	};

	static const int data_swap_select[32] =
	{
		0,0,1,1,2,2,2,3,3,4,4,5,5,5,6,6,
		7,7,7,8,8,9,9,10,10,10,11,11,12,12,12,13,
	};

	static const unsigned char swaptable[22][4] =
	{
		{ 6,4,2,0 },
		{ 4,6,2,0 },
		{ 2,4,6,0 },
		{ 0,4,2,6 },
		{ 6,2,4,0 },
		{ 6,0,2,4 },
		{ 6,4,0,2 },
		{ 2,6,4,0 },

		{ 4,2,6,0 },
		{ 4,6,0,2 },
		{ 6,0,4,2 },
		{ 0,6,4,2 },
		{ 4,0,6,2 },
		{ 0,4,6,2 },
		{ 6,2,0,4 },
		{ 2,6,0,4 },

		{ 0,6,2,4 },
		{ 2,0,6,4 },
		{ 0,2,6,4 },
		{ 4,2,0,6 },
		{ 2,4,0,6 },
		{ 4,0,2,6 }
/*		{ 2,0,4,6 }*/
/*		{ 0,2,4,6 }*/
	};
	int A;
	unsigned char *rom = memory_region(REGION_CPU1);
	int diff = memory_region_length(REGION_CPU1) / 2;


	memory_set_opcode_base(0,rom+diff);

	for (A = 0x0000;A < 0x8000;A++)
	{
		int row;
		unsigned char src;
		const unsigned char *tbl;


		src = rom[A];

		/* pick the translation table from bits 0, 3, 6, 9, 12 and 14 of the address */
		row = (A & 1) + (((A >> 3) & 1) << 1) + (((A >> 6) & 1) << 2)
				+ (((A >> 9) & 1) << 3) + (((A >> 12) & 1) << 4) + (((A >> 14) & 1) << 5);

		/* decode the opcodes */
		tbl = swaptable[opcode_swap_select[row & 0x1f] + 8 * ((row & 0x20) >> 5)];
		rom[A + diff] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ opcode_xor[row & 0x1f];

		/* decode the data */
		tbl = swaptable[data_swap_select[row & 0x1f] + 8 * ((row & 0x20) >> 5)];
		rom[A] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ data_xor[row & 0x1f];
	}

	/* copy the opcodes from the not encrypted part of the ROMs */
	for (A = 0x8000;A < diff;A++)
		rom[A + diff] = rom[A];
}


void wboy2_decode(void)
{
	static unsigned char opcode_xor[32] =
	{
		0x00,0x45,0x11,0x01,0x44,0x10,0x55,0x05,0x41,0x14,0x04,0x40,0x15,0x51,0x01,0x44,
		0x10,0x00,0x45,0x11,0x54,0x04,0x40,0x15,0x05,0x41,0x14,0x50,0x00,0x45,0x11,0x01,
	};

	static unsigned char data_xor[32] =
	{
		0x55,0x05,0x41,0x14,0x50,0x00,0x15,0x51,0x01,0x44,0x10,0x55,0x05,0x11,0x54,0x04,
		0x40,0x15,0x51,0x01,0x14,0x50,0x00,0x45,0x11,0x54,0x04,0x10,0x55,0x05,0x41,0x14,
	};

	static const int opcode_swap_select[32] =
	{
		2,
		5,1,5,1,5,
		0,4,0,4,0,4,

		7,3,7,3,7,3,
		6,2,6,2,6,
		1,5,1,5,1,5,
		0,4,0
	};

	static const int data_swap_select[32] =
	{
		3,7,3,7,3,7,
		2,6,2,6,2,
		5,1,5,1,5,1,
		4,0,4,0,4,

		8,
		3,7,3,7,3,
		6,2,6,2
	};

	static const unsigned char swaptable[17][4] =
	{
		/* note that this is a subset of the astrofl table */
		{ 6,4,2,0 },
		{ 4,6,2,0 },
		{ 2,4,6,0 },
		{ 0,4,2,6 },
		{ 6,2,4,0 },
		{ 6,0,2,4 },
		{ 6,4,0,2 },
		{ 2,6,4,0 },

		{ 4,2,6,0 },
		{ 4,6,0,2 },
		{ 6,0,4,2 },
		{ 0,6,4,2 },
		{ 4,0,6,2 },
		{ 0,4,6,2 },
		{ 6,2,0,4 },
		{ 2,6,0,4 },

		{ 0,6,2,4 }
	};
	int A;
	unsigned char *rom = memory_region(REGION_CPU1);
	int diff = memory_region_length(REGION_CPU1) / 2;


	memory_set_opcode_base(0,rom+diff);

	for (A = 0x0000;A < 0x8000;A++)
	{
		int row;
		unsigned char src;
		const unsigned char *tbl;


		src = rom[A];

		/* pick the translation table from bits 0, 3, 6, 9, 12 and 14 of the address */
		row = (A & 1) + (((A >> 3) & 1) << 1) + (((A >> 6) & 1) << 2)
				+ (((A >> 9) & 1) << 3) + (((A >> 12) & 1) << 4) + (((A >> 14) & 1) << 5);

		/* decode the opcodes */
		tbl = swaptable[opcode_swap_select[row & 0x1f] + 8 * ((row & 0x20) >> 5)];
		rom[A + diff] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ opcode_xor[row & 0x1f];

		/* decode the data */
		tbl = swaptable[data_swap_select[row & 0x1f] + 8 * ((row & 0x20) >> 5)];
		rom[A] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ data_xor[row & 0x1f];
	}

	/* copy the opcodes from the not encrypted part of the ROMs */
	for (A = 0x8000;A < diff;A++)
		rom[A + diff] = rom[A];
}


/* Gardia / Space Position */
/******************************************************************************

  New encryption

  This encryption is quite different from the older one. It permutates bits
  D0, D2, D4 and D6, then inverts some of them.

  The permutation and inversion depend on A0, A3, A6, A9, A12, and A14.

******************************************************************************/

static void sega_decode_2(const unsigned char opcode_xor[64],const int opcode_swap_select[64],
		const unsigned char data_xor[64],const int data_swap_select[64])
{
	int A;
	unsigned char *rom = memory_region(REGION_CPU1);
	int diff = memory_region_length(REGION_CPU1) / 2;
	static const unsigned char swaptable[24][4] =
	{
		{ 6,4,2,0 }, { 4,6,2,0 }, { 2,4,6,0 }, { 0,4,2,6 },
		{ 6,2,4,0 }, { 6,0,2,4 }, { 6,4,0,2 }, { 2,6,4,0 },
		{ 4,2,6,0 }, { 4,6,0,2 }, { 6,0,4,2 }, { 0,6,4,2 },
		{ 4,0,6,2 }, { 0,4,6,2 }, { 6,2,0,4 }, { 2,6,0,4 },
		{ 0,6,2,4 }, { 2,0,6,4 }, { 0,2,6,4 }, { 4,2,0,6 },
		{ 2,4,0,6 }, { 4,0,2,6 }, { 2,0,4,6 }, { 0,2,4,6 },
	};


	memory_set_opcode_base(0,rom+diff);

	for (A = 0x0000;A < 0x8000;A++)
	{
		int row;
		unsigned char src;
		const unsigned char *tbl;


		src = rom[A];

		/* pick the translation table from bits 0, 3, 6, 9, 12 and 14 of the address */
		row = (A & 1) + (((A >> 3) & 1) << 1) + (((A >> 6) & 1) << 2)
				+ (((A >> 9) & 1) << 3) + (((A >> 12) & 1) << 4) + (((A >> 14) & 1) << 5);

		/* decode the opcodes */
		tbl = swaptable[opcode_swap_select[row]];
		rom[A + diff] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ opcode_xor[row];

		/* decode the data */
		tbl = swaptable[data_swap_select[row]];
		rom[A] = BITSWAP8(src,7,tbl[0],5,tbl[1],3,tbl[2],1,tbl[3]) ^ data_xor[row];
	}

	/* copy the opcodes from the not encrypted part of the ROMs */
	for (A = 0x8000;A < diff;A++)
		rom[A + diff] = rom[A];
}

/******************************************************************************

  These games (all 317-xxxx CPUs) use the same algorithm, but the key doesn't
  change much - just one or two positions shift in the table.

******************************************************************************/

static void sega_decode_317(int order, int opcode_shift, int data_shift)
{
	static const unsigned char xor1_317[1+64] =
	{
		0x54,
		0x14,0x15,0x41,0x14,0x50,0x55,0x05,0x41,0x01,0x10,0x51,0x05,0x11,0x05,0x14,0x55,
		0x41,0x05,0x04,0x41,0x14,0x10,0x45,0x50,0x00,0x45,0x00,0x00,0x00,0x45,0x00,0x00,
		0x54,0x04,0x15,0x10,0x04,0x05,0x11,0x44,0x04,0x01,0x05,0x00,0x44,0x15,0x40,0x45,
		0x10,0x15,0x51,0x50,0x00,0x15,0x51,0x44,0x15,0x04,0x44,0x44,0x50,0x10,0x04,0x04,
	};

	static const unsigned char xor2_317[2+64] =
	{
		0x04,
		0x44,
		0x15,0x51,0x41,0x10,0x15,0x54,0x04,0x51,0x05,0x55,0x05,0x54,0x45,0x04,0x10,0x01,
		0x51,0x55,0x45,0x55,0x45,0x04,0x55,0x40,0x11,0x15,0x01,0x40,0x01,0x11,0x45,0x44,
		0x40,0x05,0x15,0x15,0x01,0x50,0x00,0x44,0x04,0x50,0x51,0x45,0x50,0x54,0x41,0x40,
		0x14,0x40,0x50,0x45,0x10,0x05,0x50,0x01,0x40,0x01,0x50,0x50,0x50,0x44,0x40,0x10,
	};

	static const int swap1_317[1+64] =
	{
		 7,
		 1,11,23,17,23, 0,15,19,
		20,12,10, 0,18,18, 5,20,
		13, 0,18,14, 5, 6,10,21,
		 1,11, 9, 3,21, 4, 1,17,
		 5, 7,16,13,19,23,20, 2,
		10,23,23,15,10,12, 0,22,
		14, 6,15,11,17,15,21, 0,
		 6, 1, 1,18, 5,15,15,20,
	};

	static const int swap2_317[2+64] =
	{
		 7,
		12,
		18, 8,21, 0,22,21,13,21,
		20,13,20,14, 6, 3, 5,20,
		 8,20, 4, 8,17,22, 0, 0,
		 6,17,17, 9, 0,16,13,21,
		 3, 2,18, 6,11, 3, 3,18,
		18,19, 3, 0, 5, 0,11, 8,
		 8, 1, 7, 2,10, 8,10, 2,
		 1, 3,12,16, 0,17,10, 1,
	};

	if (order)
		sega_decode_2( xor2_317+opcode_shift, swap2_317+opcode_shift, xor1_317+data_shift, swap1_317+data_shift );
	else
		sega_decode_2( xor1_317+opcode_shift, swap1_317+opcode_shift, xor2_317+data_shift, swap2_317+data_shift );
}

void spcpostn_decode(void)	{ sega_decode_317( 0, 0, 1 ); }
void gardia_decode(void)	{ sega_decode_317( 1, 1, 1 ); }
void gardiab_decode(void)	{ sega_decode_317( 0, 1, 2 ); }
